import CameraData from "../data/CameraData"
import Matrix from "../three/Matrix"
import * as THREE from "three"
import lineEnum from "../enum/line.enum"
import configConst from "../constant/config.const"
import se from "../math/69626"
import Grouper from "../utils/Grouper"
import { Subscription } from "../utils/Subscription"
import LinesModule, { LineEndpointMaterial, LineMaterial } from "../module/LinesModule"
import { RenderLayer } from "../utils/RenderLayers"
import MeasurementModeModule from "../module/MeasurementModeModule"
var k = {
  1: "ADDED",
  2: "REMOVED",
  4: "UPDATED",
  7: "ALL",
  ADDED: 1,
  ALL: 7,
  REMOVED: 2,
  UPDATED: 4
}
var ie = {
  255: "z",
  32768: "y",
  16711680: "x",
  16711935: "xz",
  16724312: "laser",
  16776960: "yellow",
  16777215: "white",
  free: 16777215,
  laser: 16724312,
  white: 16777215,
  x: 16711680,
  xz: 16711935,
  y: 32768,
  yellow: 16776960,
  z: 255
}
export default class MeasurementLineRenderer {
  points: Grouper
  createPointSubscription: (...e) => Subscription
  cameraData: CameraData
  lineModule: LinesModule
  mainLayer: RenderLayer
  lineLayer: RenderLayer
  selectedGroup: MeasurementModeModule["getSelected"]
  getLineDetails: Function
  lineColor: number
  endpointGeometry: THREE.PlaneGeometry
  linePool: any[]
  groupToLines: {}
  pointToLines: {}
  activeLines: any[]
  cameraQuaternion: THREE.Quaternion
  cameraPosition: THREE.Vector3
  cameraProjection: Matrix
  getLinesForPoint: (e: number) => any[]
  dataSubs: Subscription[]
  lineMaterial: LineMaterial
  endpointMaterial: LineEndpointMaterial
  dottedLineMaterial: LineMaterial
  xLineMaterial: LineMaterial
  yLineMaterial: LineMaterial
  zLineMaterial: LineMaterial
  xzLineMaterial: LineMaterial
  constructor(e, t, i, s, n, r, o = () => -1, a, h = 16777215) {
    this.points = e
    this.createPointSubscription = t
    this.cameraData = i
    this.lineModule = s
    this.mainLayer = n
    this.lineLayer = r
    this.selectedGroup = o
    this.getLineDetails = a
    this.lineColor = h
    this.endpointGeometry = new THREE.PlaneBufferGeometry(1, 1)
    this.linePool = []
    this.groupToLines = {}
    this.pointToLines = {}
    this.activeLines = []
    this.cameraQuaternion = new THREE.Quaternion()
    this.cameraPosition = new THREE.Vector3()
    this.cameraProjection = new Matrix()
    this.getLinesForPoint = (e: number) => {
      const t = []
      if (this.pointToLines[e])
        for (const i of this.pointToLines[e]) {
          const e = i.getMesh(lineEnum.line),
            { startIndex: s, endIndex: n, group: r } = e.userData
          t.push({ endIndex: n, startIndex: s, line: i, group: r })
        }
      return t
    }
    this.updateMaterialColors(this.lineColor)
    this.dataSubs = [
      this.createPointSubscription(k.REMOVED, () => {
        this.resetLines()
      })
    ]
  }
  updateMaterialColors(e: number) {
    const t = configConst.lineConfig.lineDefault
    this.lineMaterial = this.lineModule.makeLineMaterial(e, !0, {
      linewidth: t
    })
    this.lineMaterial.stencilRef = 1
    this.lineMaterial.stencilFail = THREE.KeepStencilOp
    this.lineMaterial.stencilZFail = THREE.KeepStencilOp
    this.lineMaterial.stencilZPass = THREE.KeepStencilOp
    this.lineMaterial.stencilFunc = THREE.GreaterStencilFunc
    this.lineMaterial.stencilWrite = !0
    this.endpointMaterial = this.lineModule.makeEndpointMaterial(e)
    const i = {
      dashed: !0,
      dashSize: 0.025,
      gapSize: 0.05,
      linewidth: configConst.lineConfig.dottedLineDefault
    }
    this.dottedLineMaterial = this.lineModule.makeLineMaterial(e, !1, i)
    this.xLineMaterial = this.lineModule.makeLineMaterial(ie.x, !1, i)
    this.yLineMaterial = this.lineModule.makeLineMaterial(ie.y, !1, i)
    this.zLineMaterial = this.lineModule.makeLineMaterial(ie.z, !1, i)
    this.xzLineMaterial = this.lineModule.makeLineMaterial(ie.xz, !1, i)
  }
  setLineOpacityByGroup(e, t) {
    const i = this.groupToLines[e]
    if (i) for (const e of i) e.opacity(t)
  }
  setLineOpacityByPoint(e: number, t: number) {
    const i = this.getLinesForPoint(e)
    if (i) for (const e of i) e.line.opacity(t)
  }
  resetLines() {
    for (const e of this.lines) e.opacity(0), e.hide()
    this.groupToLines = {}
    this.pointToLines = {}
    this.lines.length = 0
  }
  updateAllLines() {
    if (!(this.points.length < 1)) {
      for (let e = 0; e < this.points.length; e++) this.updateLine(e)
      for (const e in this.groupToLines) {
        const t = Number(e),
          i = this.groupToLines[t]
        for (const e of i) e.updateSelected(this.selectedGroup() === t)
      }
    }
  }
  init() {}
  dispose() {
    this.deactivate()
    for (const e of this.linePool) e.dispose()
    this.endpointGeometry.dispose()
    this.dottedLineMaterial.dispose()
    this.lineMaterial.dispose()
    this.endpointMaterial.dispose()
    this.xLineMaterial.dispose()
    this.yLineMaterial.dispose()
    this.zLineMaterial.dispose()
  }
  activate() {
    for (const e of this.dataSubs) e.renew()
  }
  deactivate() {
    for (const e of this.dataSubs) e.cancel()
    this.resetLines()
  }
  get lines() {
    return this.activeLines
  }
  get dottedMaterial() {
    return this.dottedLineMaterial
  }
  beforeRender() {
    this.cameraQuaternion.copy(this.cameraData.pose.rotation)
    this.cameraPosition.copy(this.cameraData.pose.position)
    this.cameraProjection.copy(this.cameraData.pose.projection)
  }
  get xMaterial() {
    return this.xLineMaterial
  }
  get yMaterial() {
    return this.yLineMaterial
  }
  get zMaterial() {
    return this.zLineMaterial
  }
  get xzMaterial() {
    return this.xzLineMaterial
  }
  render() {
    this.updateAllLines()
  }
  updateLine(e) {
    const t = this.points.get(e),
      i = this.points.get(e + 1),
      s = this.points.groupFromPointIndex(e) === this.points.groupFromPointIndex(e + 1)
    t && i && s && this.setLinePosition(e, t, i)
  }
  setLinePosition(e, t, i) {
    let s = this.linePool[e]
    if (!s) {
      const n = this.points.groupFromPointIndex(e)
      if (!this.getLineDetails(n, e, e + 1).visible) return
      const r = configConst.lineConfig.endpointDefault > 0.01 ? this.endpointMaterial.clone() : void 0
      ;(s = this.lineModule.makeLine(t, i, this.lineMaterial.clone(), r, () => !se.Pp(this.cameraProjection))),
        this.setupLine(s, e),
        s.setRenderLayer(this.mainLayer)
    }
    s.visible || this.setupLine(s, e),
      (this.linePool[e] = s),
      s.updateResolution(this.cameraData.width, this.cameraData.height),
      s.updatePositions(t, i),
      s.updateBillboard({
        rotation: this.cameraQuaternion,
        position: this.cameraPosition,
        projection: this.cameraProjection
      })
  }
  setupLine(e, t) {
    const i = this.points.groupFromPointIndex(t)
    e.children.forEach(e => {
      e.userData.startIndex = t
      e.userData.endIndex = t + 1
      e.userData.group = i
      e.layers.mask = this.mainLayer.mask
    }),
      (e.getMesh(lineEnum.line).layers.mask = this.lineLayer.mask),
      this.activeLines.push(e),
      this.addLineToGroup(i, e),
      this.addLineToPoint(t, e),
      this.addLineToPoint(t + 1, e),
      e.show(),
      e.opacity(0)
  }
  addLineToGroup(e, t) {
    this.groupToLines[e] || (this.groupToLines[e] = []), this.groupToLines[e].push(t)
  }
  addLineToPoint(e, t) {
    this.pointToLines[e] || (this.pointToLines[e] = []), this.pointToLines[e].push(t)
  }
}
